#include "slab.h"
#include "mmpage.h"
#include "buddy.h"
#include "stdio.h"
#include "list.h"
#include "string.h"

struct list_head slab_caches;
struct kmem_cache *kmem_cache;



static inline int obj_in_slab(struct page* p, void *objp){
    unsigned int slab_size = POWER2(p->slab_cache->gfporder)<<CONFIG_PAGE_SHIFT;

    if((unsigned long)objp >= (unsigned long)p->real_page 
        && (unsigned long)objp <= (unsigned long)((void *)p->real_page + slab_size)){
        return 1;
    }
    return 0;
}

static inline void free_obj(struct page* p ,void *objp){
    unsigned int obj_pos = (objp - p->s_mem)/p->slab_cache->object_size;
    unsigned int* pfree = p->freelist;
    // printf("a.acitve = %d \n",p->active);
    //将obj归还，并记录到-active的位置上
    pfree[--(p->active)] = obj_pos;
    p->slab_cache->node.free_objects++;
}

//move slab ==> 
//flag 参考slab_move_flag

static inline void move_slab(struct list_head *new, struct list_head* head,enum slab_move_flag flag){
    list_del(new);
    list_add_tail(new,head);
    
    struct page* p = list_entry(new, struct page, slab_list);
    switch (flag)
    {
    case SLAB_MOVE_FLAG_FREE_TO_PARTIAL:
        p->slab_cache->node.free_slabs--;
        p->slab_cache->node.partial_slabs++;
        break;
    
    case SLAB_MOVE_FLAG_PARTIAL_TO_FULL:
        p->slab_cache->node.partial_slabs--;
        p->slab_cache->node.full_slabs++;
        break;

    case SLAB_MOVE_FLAG_FULL_TO_PARTIAL:
        p->slab_cache->node.full_slabs--;
        p->slab_cache->node.partial_slabs++;
        break;

    case SLAB_MOVE_FLAG_PARTIAL_TO_FREE:
        p->slab_cache->node.partial_slabs--;
        p->slab_cache->node.free_slabs++;
        break;
    default:
        printf("unkown move type. \n");
        break;
    }
}

static inline void prepare_slab_collection(struct kmem_cache *cachep ,struct page * page){
    //将slab从队列中删除
    list_del(&page->slab_list);
    //给order赋值用于buddy回收的时候使用
    page->private = cachep->gfporder;
    //初始化buddy中的list
    INIT_LIST_HEAD(&page->list);
}

static inline void init_slab_page(struct kmem_cache *cachep,struct page* page){
    //初始化slab
    page->slab_cache = cachep;
    page->active = 0;
    page->freelist = page->real_page;      //page转换成虚拟地址
    page->s_mem = (unsigned int *)page->real_page + cachep->object_num;   //将指针指向第一个obj

    //初始化freelist的数组
    unsigned int *pfree = page->freelist;
    for (size_t i = 0; i < cachep->object_num; i++)
    {   
        pfree[i] = i;
    }

    list_add_tail(&page->slab_list,&cachep->node.slabs_free);

    //添加相关信息到node中
    cachep->node.total_slabs++;
    cachep->node.free_slabs++;
    cachep->node.free_objects += cachep->object_num;
}
void show_slab_info(){
    //遍历双向链表
    struct list_head *entry;
    list_for_each(entry,&slab_caches){
        struct kmem_cache *cachep =  list_entry(entry,struct kmem_cache,list);
        printf("--> cache(name = %s objsize = %d objnum = %d ) \n",
                cachep->name,cachep->object_size,cachep->object_num);
    }
}

void show_slab_node_info(struct kmem_cache *cachep){
    printf("========================debug========================\n");
    printf("cache.name = %s ==> \n\tslabs.all = %lu\n\tslabs.free = %lu \n\tslabs.partial = %lu \n\tslabs.full = %lu \n",
            cachep->name,
            cachep->node.total_slabs,
            cachep->node.free_slabs,
            cachep->node.partial_slabs,
            cachep->node.full_slabs);

    printf("\tfree_list :");
    struct list_head* entry;

    list_for_each(entry, &cachep->node.slabs_free){
        struct page*p = list_entry(entry,struct page,slab_list);
        printf("--> %x {active = %d } ",p,p->active);
    }
    printf("\n");

    printf("\tpartial_list :");
    list_for_each(entry, &cachep->node.slabs_partial){
        struct page*p = list_entry(entry,struct page,slab_list);
        printf("--> %x {active = %d } ",p,p->active);
    }
    printf("\n");

    printf("\tfull_list :");
    list_for_each(entry, &cachep->node.slabs_full){
        struct page*p = list_entry(entry,struct page,slab_list);
        printf("--> %x {active = %d } ",p,p->active);
    }
    printf("\n");
    printf("========================debug========================\n");
}

void slab_init(){
    kmem_cache = NULL;
    INIT_LIST_HEAD(&slab_caches);
    kmem_cache_init(&kmem_cache_boot);
}

void kmem_cache_init(struct kmem_cache *cachep){
    if(kmem_cache == NULL){
        kmem_cache = cachep;
    }

    INIT_LIST_HEAD(&cachep->list);
    INIT_LIST_HEAD(&cachep->node.slabs_free);
    INIT_LIST_HEAD(&cachep->node.slabs_partial);
    INIT_LIST_HEAD(&cachep->node.slabs_full);

    //将kmem_cache添加到slab_caches中
    list_add(&cachep->list,&slab_caches);

    //计算一个页面可以容纳多少个obj
    int cell = sizeof(int) + cachep->object_size;
    int objs = CONFIG_PAGE_SIZE / cell;
    cachep->object_num = objs;

}


struct kmem_cache *kmem_cache_create(const char *name,unsigned int size,unsigned int align,int flags,void(*ctor)(void *)){
    //创建一个slab节点:从哪里申请的内存呢？
    //linux使用slab_common中的接口来申请kmem_cache的节点
    //申请一个kmem_cache节点
    struct kmem_cache* cachep = (struct kmem_cache*)kmem_cache_alloc(kmem_cache,0);
    cachep->name = name;
    cachep->object_size = size;
    cachep->align = align;
    cachep->ctor = ctor;
    cachep->node.free_limit = 1;
    cachep->gfporder = 1;
    kmem_cache_init(cachep);
}


//kmem_cache destory的流程：
//1.首先将当前cachep的slab{free\partial\full}
//2.将cache从链表中删去
//3.所有cachep使用的数据结构对象都是从kmem_cache_boot申请的 将该对象从boot中free
void kmem_cache_destory(struct kmem_cache * cachep){
    //释放一个kmen_cache.
    //先释放内部node的slab
    struct list_head *entry;
    struct list_head *next;

    //这里重复的3部分可以考虑用宏来代替展开
    if(cachep->node.free_slabs){
        list_for_each_safe(entry,next,&cachep->node.slabs_free){
            struct page* p = list_entry(entry,struct page,slab_list);
            prepare_slab_collection(cachep,p);
            buddy_free_pages(p);
        }
    }
    
    if(cachep->node.partial_slabs){
        list_for_each_safe(entry,next,&cachep->node.slabs_partial){
            struct page* p = list_entry(entry,struct page,slab_list);
            prepare_slab_collection(cachep,p);
            buddy_free_pages(p);
        }
    }

    if(cachep->node.full_slabs){
        list_for_each_safe(entry,next,&cachep->node.slabs_full){
            struct page* p = list_entry(entry,struct page,slab_list);
            prepare_slab_collection(cachep,p);
            buddy_free_pages(p);
        }
    }

 
    list_del(&cachep->list);
    //所有slab都已经回收了  将cache从链表中移除
    kmem_cache_free(kmem_cache,cachep);
}


void *kmem_cache_alloc(struct kmem_cache *cachep,int flags){
    int flag_get_free = 0;
    // printf("kmem_cache_alloc call ==> node.free_objects = %d  \n",cachep->node.free_objects);
    void *result = 0;
    if(cachep->node.free_objects == 0){
        // printf("free_objects == 0 call buddy_alloc_pages \n");
        //该kmem还没有申请空间,根据其gfporder像buddy申请空间
        struct page* page = buddy_alloc_pages(cachep->gfporder);
        if(page == 0){
            return 0;
        }
        init_slab_page(cachep,page);
    }

    //运行到此处cachep中一定有free obj可以获得
    struct list* entry = cachep->node.slabs_partial.next;
    if(list_empty(entry)){
        //partial的逻辑
        entry = cachep->node.slabs_free.next;
        if(list_empty(entry)){
            printf("error ==> slabs_free is null\n");
            return 0;
        }
    }

    //在这里要判断一下entry是哪个链表的partial/free
    struct page* p = list_entry(entry,struct page,slab_list);
    unsigned int *pfree = (unsigned int *)p->freelist;
    unsigned int target = 0;
    if(p->active < cachep->object_num){
        //p->active指向的freelist的下标的value就是第几个obj可以使用
        unsigned int target  = pfree[p->active++];
        result = p->s_mem + (target*cachep->object_size);
        cachep->node.free_objects--;
    }
    
    //分配完成后检查该slab是否还有可使用的objs
    if(p->active >= cachep->object_num){
        // printf("move list from partial into full\n");
        //将p从slab_partial中拿掉 放到slabs_full中
        move_slab(&p->slab_list,
                    &cachep->node.slabs_full,
                    SLAB_MOVE_FLAG_PARTIAL_TO_FULL);
        show_slab_node_info(cachep);
    }

    //申请的时候如果active=1则一定是从active=0(free) 变成active=1(partial)
    if(p->active == 1){
        // printf("move list from free into partial \n");
        move_slab(&p->slab_list,
                    &cachep->node.slabs_partial,
                    SLAB_MOVE_FLAG_FREE_TO_PARTIAL);
        show_slab_node_info(cachep);
    }
    return result;
}



void kmem_cache_free(struct kmem_cache* cachep,void* objp){
    //在相应cachep.node的partial中寻找地址是否在其中，如果没找
    //就在full中寻找
    struct list_head *entry;

    //在partial中寻找
    list_for_each(entry,&cachep->node.slabs_partial){
        struct page* p = list_entry(entry,struct page,slab_list);
        if(obj_in_slab(p,objp)){
            // printf("find objp in partial \n");
            //在partial中找到该节点，free
            free_obj(p,objp);
            if(p->active == 0){
                // printf("acitve = 0 move slab from partial into free \n");
                //检查node.free_limit-> 如果超过这个值直接触发buddy_free
                if(cachep->node.free_slabs >= cachep->node.free_limit){
                    //触发回收
                    prepare_slab_collection(cachep,p);
                    buddy_free_pages(p);
                }else{
                    //不回收 放回队列中
                    move_slab(&p->slab_list,
                                &cachep->node.slabs_free,
                                SLAB_MOVE_FLAG_PARTIAL_TO_FREE);
                }   
            }
            return;
        }
    }

    //在full中寻找
    //使用list_for_each可能没法正确的移动链表
    list_for_each(entry,&cachep->node.slabs_full){
        struct page* p = list_entry(entry,struct page,slab_list);
        if(obj_in_slab(p,objp)){
            // printf("find objp in full \n");
            //在full中找到该节点，free
            free_obj(p,objp);
            // printf("move slab from free into partial \n");
            move_slab(&p->slab_list,
                        &cachep->node.slabs_partial,
                        SLAB_MOVE_FLAG_FULL_TO_PARTIAL);
            return;
        }
    }
    printf("fail to find objp slab. \n");
}

#define ALLOC_TEST_NUM 100

void slab_test(struct kmem_cache *cachep){

    unsigned long alloc_back[ALLOC_TEST_NUM];
    int idx = 0;
    for (size_t i = 0; i < ALLOC_TEST_NUM; i++)
    {
        void *p = kmem_cache_alloc(cachep,0);
        alloc_back[idx++] = (unsigned long)p;
        // printf("kmem_cache_alloc result = %x \n",p);
    }
    printf("slab test alloc done \n");
    show_slab_node_info(cachep);

    
    for (int i = 0; i < ALLOC_TEST_NUM; i++)
    {
        kmem_cache_free(cachep,(void *)alloc_back[i]);
    }
    printf("slab test free done \n");
    show_slab_node_info(cachep);
}